<?php

/**
 * The main KeyGenius class. This should be the only class you need to
 * call from the Controller. It wraps the individual ORM models and makes 
 * sure you're not doing something unsafe, such as returning passwords 
 * without a valid token.
 * 
 * @author Dain Nilsson <dain@keygeni.us>
 */
class KeyGenius {
	const TOKEN = 'token';
	const URL = 'url';
	const CALLBACK = 'callback';
	const STORE = 'store';
	const GENERATE = 'gen';

	/**
	 * Deletes a user and all if his/her stored data permanently. 
	 * Once revoked, the YubiKey can again be used to store new data.
	 * 
	 * @param string $code A previously generated revocation code.
	 * 
	 * @return boolean True if the code was accepted and a user was removed, false if not.
	 */
	public static function revokeUser($code) {
		return Doctrine::getTable('Owner')->revokeOneByCode($code);
	}

	/**
	 * Generated a revocation code to be used with revokeUser.
	 * If the user had previously generated a code with this function,
	 * that code is invalidated.
	 * 
	 * @param string $token A YubiKey OTP beloning to the user to generate a code for.
	 * 
	 * @return string If successful, returns the generated code. If the OTP is invalid, returns null.
	 */
	public static function generateRevocation($token) {
		$owner = Doctrine::getTable('Owner')->findOrCreateOwnerByYubi($token);
		return $owner->generateRevocationCode();
	}

	/**
	 * Stores a password for a user under the given domain.
	 * 
	 * @param string $token A YubiKey OTP belonging to the user to store the password for.
	 * @param string $domain The domain for which to store the password.
	 * 
	 * @return boolean True if the code was accepted and a user was removed, false if not.
	 */
	public static function storePassword($token, $domain, $password) {
		$owner = Doctrine::getTable('Owner')->findOrCreateOwnerByYubi($token);
		return $owner->setPasswordForDomain($domain, $password)===null ? false : true;
	}

	/**
	 * Deletes a password for a user under the given domain.
	 *
	 * @param string $token A YubiKey OTP belonging to the user from which to delete the password.
	 * @param integer $id The ID of the password to delete.
	 * 
	 * @return array A list of the users passwords, same as listPasswords.
	 */
	public static function deletePassword($token, $id) {
		$owner = Doctrine::getTable('Owner')->findOrCreateOwnerByYubi($token);
		$pw = Doctrine::getTable('Password')->find($id);
		if($pw->Owner == $owner) {
			$owner->setPasswordForDomain($pw->domain, null);
		}
		$pws = array();
		foreach($owner->Passwords as $pw) {
			$pw->setToken($token);
			$pws[$pw->domain] = array('id' => $pw->id, 'value' => $pw->value);
		}
		return $pws;
	}

	/**
	 * Gets a list of a users passwords.
	 * 
	 * @param string $token A YubiKey OTP from the user.
	 * 
	 * @return array A list of all the users stored passwords, with domains and ids.
	 */
	public static function listPasswords($token) {
		$owner = Doctrine::getTable('Owner')->findOrCreateOwnerByYubi($token);
		$pws = array();
		if(!$owner->isYubiValid()) return $pws;
		foreach($owner->Passwords as $pw) {
			$pw->setToken($token);
			$pws[$pw->domain] = array('id' => $pw->id, 'value' => $pw->value);
		}
		return $pws;
	}

	/**
	 * Validates a YubiKey OTP.
	 * 
	 * @param string $token A YubiKey OTP.
	 * 
	 * @return boolean True if the OTP is valid, false if not.
	 */
	public static function validateToken($token) {
		$yubi = new Yubikey(sfConfig::get('app_yubico_id'), sfConfig::get('app_yubico_key'));
		return $yubi->verify($token);
	}

	/**
	 * Process a request from a client.
	 * 
	 * @param array The GET parameters passed from the client.
	 * 
	 * @return string The response to send back to the client.
	 */
	public static function processAjaxRequest($argv) {
		if(!isset($argv[self::TOKEN]) or !isset($argv[self::URL])) return false;
		$domain = self::getDomain($argv[self::URL]);
		$owner = Doctrine::getTable('Owner')->findOrCreateOwnerByYubi($argv[self::TOKEN]);
		$pw = null;
		if(isset($argv[self::GENERATE])) {
			$pw = $owner->setPasswordForDomain($domain, self::generatePassword(intval($argv[self::GENERATE])));
		} elseif(isset($argv[self::STORE])) {
			$pw = $owner->setPasswordForDomain($domain, $argv[self::STORE]);
		} else {
			$pw = $owner->getPasswordForDomain($domain);
		}

		if(isset($argv[self::CALLBACK])) {
			if($pw==null) return $argv[self::CALLBACK] . '(null);';
			return $argv[self::CALLBACK] . '("' . $pw . '");';
		} elseif($pw!==null) return $pw;
		else return false;
	}

	private static function getDomain($url) {
		$domain = str_replace(array('http://','https://'), array('',''), $url);
		$domain = current(explode('/', $domain, 2));
		if(substr($domain, 0, 4) == 'www.') $domain = substr($domain, 4);
		return strtolower($domain);
	}

	private static function generatePassword($length) {
		$pw = "";
		while($length-- > 0) {
			$pw .= substr(sfConfig::get('app_gen_chars_allowed'), mt_rand(0, sfConfig::get('app_gen_chars_count')-1), 1);
		}
		return $pw;
	}
}

?>
